/**
 *	@author: Francis Germiline Veto
 *	date created: March 13, 2005
 *	@ver: 1.02.01
 * 	change history:
 *		June 2, 2006 - kit
 *			adjusted for adjacent chunks in exploration
 *			exploreChunk(V,S,b) working vis-a-vis exploreWithAdjacentChunks(V,V,b)
 */

package text.learning;

import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import java.sql.SQLException;
import java.sql.ResultSet;

import text.obj.TExtChunk;
import text.obj.ChunkPair;
import text.util.DBConnector;
import text.util.TExtLearnerUtility;

/* UPDATE: 031906
 *	Chunk exploration finally completed. Preliminary tests show positive results. 
 * UPDATE: 051906
 *	Tested to be resilient. If there is a problem encountered with exploration
 *		of a chunk, check TExtLearner.findMatchingChunks(AS) first.
 * UPDATE: 060206
 *	exploreChunk(V,S,b) works hand-in-hand with exploreWithAdjacentChunks(V,V,b).
 *		The changes are still untested. Code is unsafe until verified by extensive
 *		testing.
 */

public class ChunkMiner {
	
	private final String _strSentinel = new String("_sentinel_");
	
	private Hashtable htAccessTable;
//	private DBConnector dbConnection;
	private boolean bSourceIsFilipino;
	private ChunkStack csMemory;
	
	private TExtLearnerUtility tlu;
	public ChunkMiner(boolean bSourceIsFilipino, TExtLearnerUtility tlu) {
		this.bSourceIsFilipino = bSourceIsFilipino;
		htAccessTable = new Hashtable();
//		dbConnection = new DBConnector();
		csMemory = new ChunkStack();
		this.tlu = tlu;
//		connectDB();
	}
/*	
	private void connectDB() {
		dbConnection.connect();
	}
*/	
	/**
	 * NOTE: An explicit call to this function must be used whenever you have
	 * finished using an instance of this class.
	 */
/*
	public void disconnectDB() {
		dbConnection.close();
	}
*/	
	/**
	 * NOTE: The hashtable memory is used for the alignments for chunk mining
	 * explorations. Call this function before a call to exploreChunk() for
	 * clean data.
	 */
	public void clearData() {
		htAccessTable.clear();
	}
	
	public Hashtable accessData() {
		return htAccessTable;
	}
	
	public Object exploreWithAdjacentChunks(Vector vExample, Vector vChunks, boolean bIsForSource) {
/*		
		if(dbConnection.isClosed())
			connectDB();
*/		
		ChunkPair wpChunkData;// = new ChunkPair(new String(),new String(), new String());
		
		//System.out.println("'GENETIC' RECURSION: "+vExample.toString());
				
		String strDomain; // = vChunks.remove(0).toString();
		
		Vector vExampleBackup[] = new Vector[vChunks.size()], 
			vBackupChunkIndices[] = new Vector[vChunks.size()],
			vChunkBuild[] = new Vector[vChunks.size()];
		for(int d=0; d<vBackupChunkIndices.length; d++)
			vBackupChunkIndices[d] = new Vector();
		
		Vector vChunksBackup = new Vector(), vChunksClone = (Vector)vChunks.clone();
		boolean bCandidate = true, bBacktracking = false;
		for(int a=0; a<vChunksClone.size() && bCandidate; a++) {
			strDomain = vChunksClone.elementAt(a).toString();
			
			boolean bFound = false;
			Vector vTestTokens = new Vector();
			for(int b=0; vExample.size()-vChunks.size()+1-b>=0 && !bFound; b++) {
				if(bBacktracking) {
					String strIndex = vBackupChunkIndices[a].elementAt(
						vBackupChunkIndices[a].size()-1).toString();
					if(strIndex.equals("0"))
						continue;
					else
						b = Integer.parseInt(strIndex) - 1;
				}
				
				vTestTokens = new Vector();
				for(int c=0; c<vExample.size()-vChunks.size()+1-b; c++)
					vTestTokens.addElement(vExample.get(c));
				if(vBackupChunkIndices[a].contains(new Integer(b)))
					continue;
				
				String strDomCopy = strDomain;
				if(strDomain.charAt(0) == '[' && strDomain.charAt(strDomain.length()-1) == ']')
					strDomCopy = strDomain.substring(1,strDomain.length()-1);
				Object oTemp = exploreChunk(vTestTokens, strDomain, bIsForSource);
	
				if(oTemp instanceof ChunkPair) {
					vExampleBackup[a] = new Vector();
					vChunkBuild[a] = new Vector();
					

					if(bIsForSource)
						vExampleBackup[a].addAll(((ChunkPair)oTemp).getSourceChunk());
					else
						vExampleBackup[a].addAll(((ChunkPair)oTemp).getTargetChunk());
					
					vChunkBuild[a].addAll(vExampleBackup[a]);
					
		//			vChunksBackup.addElement(strDomain);
					vBackupChunkIndices[a].addElement(new Integer(b));
					bFound = true;
				} else if(oTemp instanceof String) 
					continue;
				
			}
			
			if(bFound) {
				vChunksBackup.addElement(vChunksClone.elementAt(a).toString());
				if(vExample.removeAll(vTestTokens) && vChunks.remove(strDomain));
			} else {
				if(a==0) // meaning the very first chunk has no match at all
					bCandidate = false;
				else {
					vBackupChunkIndices[a] = new Vector();;
					vChunkBuild[a] = new Vector();
					
					vExample.addAll(0,vExampleBackup[a-1]);
					vChunks.insertElementAt(vChunksBackup.remove(vChunksBackup.size()-1),0);
					vExampleBackup[a-1] = new Vector();
					
					a -= 2;
				}
			}
		}
		
		if(bCandidate) {
			TExtChunk cChunk = new TExtChunk();
			Vector vTemp = new Vector();
			for(int d=0; d<vChunkBuild.length; d++)
				vTemp.addAll(vChunkBuild[d]);
			cChunk.setChunk(vTemp, null, null);
			if(bIsForSource)
				return new ChunkPair(cChunk, new TExtChunk(), null);
			else
				return new ChunkPair(new TExtChunk(), cChunk, null);
		} else
			return "none";
	}
	
	// What are the parameters? What is to be returned if the chunk is found?
	public Object exploreChunk(Vector vExample, String strDomain, boolean bIsForSource) {
/*		
		if(dbConnection.isClosed())
			connectDB();
*/		
		ChunkPair wpChunkData;// = new ChunkPair(new String(),new String(), new String());
		if(vExample.size() == 0 || vExample == null)
			return "[" + strDomain + "]";
		
		csMemory.push(strDomain);
		String strStmt = "Select * From Chunks Where ";
		ResultSet rsSet;
		
		if(strDomain.startsWith("[") && strDomain.endsWith("]"))
			strDomain = strDomain.substring(1, strDomain.length()-1);
		StringTokenizer stSQL = new StringTokenizer(strDomain, "/");
		boolean bFirstToken = true;
		Vector vExploreDomains = new Vector();
		while(stSQL.hasMoreTokens()) {
			if(!bFirstToken)
				strStmt += " OR ";
			String strNextDomain = stSQL.nextToken();
			if(strNextDomain.contains("."))
// TEST				strStmt += "domain=" + strNextDomain;
				strStmt += "domain Like '" + strNextDomain + "'";
			else 
				strStmt += "domain Like '" + strNextDomain + ".%'";
			vExploreDomains.addElement(strNextDomain);
			bFirstToken = false;
		}
		
				
		String strSource, strTarget, strCWIDSource, strCWIDTarget;
		if(bSourceIsFilipino) {
			strSource = "chunkFilipino";
			strTarget = "chunkEnglish";
			strCWIDSource = "compIDFilipino";
			strCWIDTarget = "compIDEnglish";
		} else {
			strSource = "chunkEnglish";
			strTarget = "chunkFilipino";
			strCWIDTarget = "compIDFilipino";
			strCWIDSource = "compIDEnglish";
		}
				
		rsSet = DBConnector.select(strStmt);
		
		TExtChunk cChunk = new TExtChunk();
		StringBuffer sbChunk = new StringBuffer();
		int nDomainCtr = -1;
		try {
			while(rsSet.next()) {
				String strChunkS = rsSet.getString(strSource);
				String strChunkT = rsSet.getString(strTarget);
				String strChunkCDS = rsSet.getString(strCWIDSource);
				String strChunkCDT = rsSet.getString(strCWIDTarget);
				String strCheckDomain = rsSet.getString("domain");
				
				if(nDomainCtr+1 < vExploreDomains.size())
					nDomainCtr++;
				if(!strCheckDomain.startsWith(vExploreDomains.elementAt(nDomainCtr).toString())
					&& nDomainCtr != 0)
					nDomainCtr--;
				
				TExtChunk cSource = new TExtChunk(strChunkS, 
					vExploreDomains.elementAt(nDomainCtr).toString(), strChunkCDS),
					cTarget = new TExtChunk(strChunkT, 
					vExploreDomains.elementAt(nDomainCtr).toString(), strChunkCDT);


				String strCheck;
				TExtChunk cCheck;
				if(bIsForSource) {
					strCheck = strChunkS;
					cCheck = cSource;
				} else {
					strCheck = strChunkT;
					cCheck = cTarget;
				}
				
			//start for compound words
				cCheck = tlu.adjustChunkCompound(cCheck);
				cCheck.setCompoundIdentifier("none");
			//end for compound words
			
				if(!strChunkS.contains("[") && !strChunkT.contains("[")
					&& !strChunkS.trim().equals("") && !strChunkT.trim().equals("")) {//meaning there's no subchunk
					int nTracker = 0, nPtr = 0;
					while(nPtr<cCheck.size() && nTracker<vExample.size()
						&& cCheck.elementAt(nPtr) instanceof String 
						&& cCheck.elementAt(nPtr).toString()
						.equalsIgnoreCase(vExample.elementAt(nTracker).toString())) {
						nTracker++;
						nPtr++;
					}
					if(nPtr==cCheck.size() && nPtr==vExample.size()) // MODIFICATION
						if(bIsForSource)
							return new ChunkPair(cCheck, new TExtChunk(), 
								vExploreDomains.elementAt(nDomainCtr).toString()); // MODIFICATION
						else
							return new ChunkPair(new TExtChunk(), cCheck,
								vExploreDomains.elementAt(nDomainCtr).toString()); // MODIFICATION
			/*		else
						return "[" + strDomain + "]";
						//strStuff = strCheck;		*/
				} else if(strChunkS.contains("[") && strChunkT.contains("[")) {
										
					boolean bPreviousWasChunk = false, bStarved = false;
					String strExCheck = new String();
					Object oTemp = null, oComp = null, oResult = null;
					int eNum = 0;
					for(int cNum=0; cNum<cCheck.size() || bStarved; cNum++) {

						oComp = cCheck.get(cNum);
						
						if(oComp instanceof String) {
							try{
								strExCheck = (String)vExample.elementAt(eNum);
							}catch(ArrayIndexOutOfBoundsException ee){		
								continue;
							}catch(Exception ee){
								continue;
							}
							

							int ctr = eNum;

							if(oComp.toString().equalsIgnoreCase(strExCheck)) {
								eNum = ctr;
								cChunk.addElement(oComp.toString());
								sbChunk.append(oComp.toString()).append(' ');
								eNum++;
							} else
								return "[" + strDomain + "]";
							bStarved = false;
							bPreviousWasChunk = false;
						} else {
							
							Object oNextTok = null, oNextExTok = null;
							String strNextTok = null, strNextExTok = null;
							
							boolean bPreStarved = false;
							if(cNum<cCheck.size()-1) {
								oNextTok = cCheck.get(cNum+1);
								bPreStarved = true;
							}
							
							Vector vChunkEx = new Vector();
							if(oNextTok != null && oNextTok instanceof String) {
								try{
									oNextExTok = vExample.get(eNum);							
								}catch(ArrayIndexOutOfBoundsException ee){	
									continue;
								}catch(Exception ee){
									continue;
								}

								eNum++;
								while(eNum < vExample.size() 
									&& oNextExTok != null
									&& !oNextTok.toString().equalsIgnoreCase(oNextExTok.toString())) {
									
									vChunkEx.addElement(oNextExTok);
									oNextExTok = vExample.get(eNum);
									eNum++;
								}
								/* This is on the assumption that there is always at
								 *	least one constant in between chunks; therefore
								 *	the next token is a constant.
								 */
								 
								if(!oNextTok.toString().equalsIgnoreCase(oNextExTok.toString()))
									return "[" + strDomain + "]";
								/* for TESTING
								 */
								eNum--;
							} else if(oNextTok == null) {
								while(eNum<vExample.size()) {
									vChunkEx.addElement(vExample.elementAt(eNum).toString());
									//sbChunkExample.append(vExample.elementAt(eNum).toString());
									/*if(eNum != vExample.size()-1)
										sbChunkExample.append(' ');*/
									eNum++;
								}
							}
							
							
							String strDom = oComp.toString().substring(1,oComp.toString().length()-1);
			/*		System.out.println("to recurse:"+sbChunkExample.toString()
								+","+strDom);
			*/				
							if(!(oNextTok != null && oNextTok instanceof TExtChunk)) {
								oTemp = exploreChunk((Vector)vChunkEx.clone(), 
									strDom, bIsForSource);
								String strOldDomain = (String)csMemory.pop();
								if(oTemp instanceof ChunkPair) {
									if(bIsForSource) {
										cChunk.addAll(((ChunkPair)oTemp).getSourceChunk());
										sbChunk.append(((ChunkPair)oTemp).getSourceStr());
									} else {
										cChunk.addAll(((ChunkPair)oTemp).getTargetChunk());
										sbChunk.append(((ChunkPair)oTemp).getTargetStr());
									}
									// should it be the other way around? I think not...
								} else if(oTemp instanceof String) {
									cChunk.addElement(oTemp.toString());
									sbChunk.append(oTemp.toString());
								}
							} else {
								/* If the program enters this code block, it
								 * means there are adjacent chunks to be checked
								 * independently. Therefore, it must be analyzed
								 * carefully.
								 */
								 Vector vChunkList = new Vector();
								 vChunkList.addElement(strDom);
								int nIndex = cNum+1;
								while(nIndex < cCheck.size() &&
								 	cCheck.get(nIndex) instanceof TExtChunk) {
								 	TExtChunk cNextChunk = (TExtChunk)cCheck.get(nIndex);
								 	vChunkList.addElement(cNextChunk.domain());
								 	nIndex++;
								}
								String strMarker = null;
								if(nIndex < cCheck.size() 
								 	&& cCheck.get(nIndex) instanceof String)
								 	strMarker = (String)cCheck.get(nIndex);
								 	
								if(eNum>=vExample.size())
									return "[" + strDomain + "]";
								// MOD : TO TEST
									
								Vector vTokenList = new Vector();
								int nIndexMem = eNum;
								String strPeekToken = (String)vExample.get(eNum);
								if(strMarker != null) {
									while(eNum < vExample.size() 
										&& strPeekToken != null
										&& !strPeekToken.equalsIgnoreCase(strMarker)) {
										
										vTokenList.addElement(strPeekToken);
										eNum++;
										if(eNum < vExample.size())
											strPeekToken = (String)vExample.get(eNum);
									}
									/*
									if(vTokenList.size() < vChunkList.size() &&
										strMarker.equalsIgnoreCase(strPeekToken)) {
										/* Just in case an earlier match is used
										 * that produces a shorter list of tokens.
										 *
										Vector vTokenAnnexList = new Vector();
										vTokenAnnexList.addElement(strPeekToken);
										String strPeek2Token = (String)vExample.get(eNum);;
										while(eNum < vExample.size() 
											&& strPeek2Token != null
											&& !strPeek2Token.equalsIgnoreCase(strMarker)) {
					System.out.println("--extended-->"+strPeek2Token+"--"+strMarker);
											
											vTokenAnnexList.addElement(strPeek2Token);
											eNum++;
											strPeek2Token = (String)vExample.get(eNum);
										}
										if(strPeek2Token.equalsIgnoreCase(strMarker)) 
											vTokenList.addAll(vTokenAnnexList);
									}
									*/
								} else {
									for(int b=eNum; b<vExample.size(); b++)
										vTokenList.addElement(vExample.get(b).toString());
								}
								/* MOD
								if(eNum != vExample.size())
									eNum--;
								*/
								if(strMarker != null && 
									!strPeekToken.equalsIgnoreCase(strMarker))
									return "[" + strDomain + "]";
								
								if(vTokenList.size() < vChunkList.size())
									return "[" + strDomain + "]";
								/* There is no way to divide m tokens among n
								 * chunks for m < n.
								 */
								
								// ADJACENT CHUNKS START
								oResult = exploreWithAdjacentChunks((Vector)vTokenList.clone(), 
									(Vector)vChunkList.clone(), bIsForSource);
								if(oResult instanceof ChunkPair) {
									
									if(bIsForSource) {
										cChunk.addAll(((ChunkPair)oResult).getSourceChunk());
										sbChunk.append(((ChunkPair)oResult).getSourceStr());
									} else {
										cChunk.addAll(((ChunkPair)oResult).getTargetChunk());
										sbChunk.append(((ChunkPair)oResult).getTargetStr());
									}
									// MOD START
									cNum += vChunkList.size()-1;
								} else {
									return "[" + strDomain + "]";
									/*
									for(int a=0; a<vChunkList.size(); a++) {
										cChunk.addElement(new TExtChunk(vChunkList.elementAt(a).toString()));
										sbChunk.append(' ').append(oTemp.toString());
									}
									*/
								}
								// ADJACENT CHUNKS END
							}
							/* FLAG
							if(bPreStarved) 
								bStarved = true;
							*/
							// MOD END
								
							bPreStarved = false;
							bPreviousWasChunk = true;
							/*oComp = oNextTok;
							strExCheck = oNextExTok.toString();	*/
						}
						
						if(bStarved)
							sbChunk.append(' ');
					}
					if((oTemp instanceof String || oTemp == null) && 
						(oResult == null || oResult instanceof String)) // MOD
						continue;
					/* At this point, it must be assumed that a chunk matches
					 * exactly to all subchunk elements. Otherwise, the code
					 * skips to the next possible chunk to explore.
					 */

					String strChunk = sbChunk.toString();
					
					if(strChunk.equals(""))
						strChunk = new String(oComp.toString());
					strChunk = strChunk.trim();
					
					if(bIsForSource) {
						htAccessTable.put(strDomain, cChunk);
						return new ChunkPair(cChunk, new TExtChunk(), 
							vExploreDomains.elementAt(nDomainCtr).toString()); // MOD
					} else {
						htAccessTable.put(strDomain, cChunk);
						return new ChunkPair(new TExtChunk(), cChunk,
							vExploreDomains.elementAt(nDomainCtr).toString()); // MOD
					}
				}
			}	
			
			
			
		} catch(SQLException sqle) {
			System.err.println("SQL Error: "+sqle.getMessage());
		}finally{
			//close here
			try{
				rsSet.close();
			}catch(Exception e){
				
			}
			
			
		}

//		if(csMemory.size()==1) {
//min's mod: not sure
		if(csMemory.size()==1 && nDomainCtr < vExploreDomains.size() && cChunk.size() > 0) {
			String strChunk = sbChunk.toString();
/*			
			if(!dbConnection.isClosed())
				disconnectDB();
*/			
			
			if(bIsForSource)
				return new ChunkPair(cChunk, new TExtChunk(), 
					vExploreDomains.elementAt(nDomainCtr).toString()); // MOD
			else
				return new ChunkPair(new TExtChunk(), cChunk,
					vExploreDomains.elementAt(nDomainCtr).toString()); // MOD
		}
				
		/* So far, the only time this point is reached is when there are totally
		 * no matches for the chunk given the example. Given such a case, just
		 * return the chunk label.
		 */
		return "["+strDomain+"]";
	}
	
	/**
	 * @deprecated as of ver. 1.01.02
	 */
	public Object retrieveChunk(Vector vExample, String strDomain,
		boolean bIsForSource) {
/*		
		if(dbConnection.isClosed())
			connectDB();
*/		
		ChunkPair wpChunkData;// = new ChunkPair(new String(),new String(), new String());
		
		System.out.println("NEW RECURSION: "+vExample.toString()+"\t"+strDomain);
		csMemory.push(strDomain);
		System.out.println("stack size:"+csMemory.size());
		String strStmt = new String();
		ResultSet rsSet;
				
		if(strDomain.contains("."))
			strStmt = "Select * From Chunks Where domain=" + strDomain;
		else 
			strStmt = "Select * From Chunks Where domain Like '" + strDomain 
				+ ".%'";
				
		String strSource, strTarget, strCWIDSource, strCWIDTarget;
		if(bSourceIsFilipino) {
			strSource = "chunkFilipino";
			strTarget = "chunkEnglish";
			strCWIDSource = "compIDFilipino";
			strCWIDTarget = "compIDEnglish";
		} else {
			strSource = "chunkEnglish";
			strTarget = "chunkFilipino";
			strCWIDTarget = "compIDFilipino";
			strCWIDSource = "compIDEnglish";
		}
				
		rsSet = DBConnector.select(strStmt);
		
		TExtChunk cChunk = new TExtChunk();
		StringBuffer sbChunk = new StringBuffer();
		//String strStuff = new String();
		try {
			while(rsSet.next()) {
				String strChunkS = rsSet.getString(strSource);
				String strChunkT = rsSet.getString(strTarget);
				String strChunkCDS = rsSet.getString(strCWIDSource);
				String strChunkCDT = rsSet.getString(strCWIDTarget);
				
				TExtChunk cSource = new TExtChunk(strChunkS, strDomain, strChunkCDS),
					cTarget = new TExtChunk(strChunkT, strDomain, strChunkCDT);


				String strCheck;
				TExtChunk cCheck;
				if(bIsForSource) {
					strCheck = strChunkS;
					cCheck = cSource;
				} else {
					strCheck = strChunkT;
					cCheck = cTarget;
				}
				
			//start for compound words
				cCheck = tlu.adjustChunkCompound(cCheck);
				cCheck.setCompoundIdentifier("none");
			//end for compound words
			
				if(!strChunkS.contains("[") && !strChunkT.contains("[")) {//meaning there's no subchunk
					System.out.println("check 1.1");
					System.out.println("source:"+strChunkS+"; target:"+strChunkT
						+"; example:"+vExample);
					int nTracker = 0, nPtr = 0;
					System.out.println(cCheck.elementAt(nPtr).toString()+"<-->"+vExample.elementAt(nTracker).toString());
					while(nPtr<cCheck.size() && nTracker<vExample.size()
						&& cCheck.elementAt(nPtr) instanceof String 
						&& cCheck.elementAt(nPtr).toString()
						.equalsIgnoreCase(vExample.elementAt(nTracker).toString())) {
						nTracker++;
						nPtr++;
					}
			System.out.println(nPtr+":::"+cCheck.size());
					if(nPtr==cCheck.size() && nPtr==vExample.size()) // MODIFICATION
						if(bIsForSource)
							return new ChunkPair(cCheck, new TExtChunk(), strDomain);
						else
							return new ChunkPair(new TExtChunk(), cCheck, strDomain);
					else
						return "[" + strDomain + "]";
						//strStuff = strCheck;
				} else if(strChunkS.contains("[") && strChunkT.contains("[")) {
					/*StringTokenizer stChunk = new StringTokenizer(strCheck);
					StringTokenizer stExample = new StringTokenizer(strExample);
					Vector vChunk = new Vector(), vExample = new Vector();
					while(stChunk.hasMoreTokens())
						vChunk.addElement(stChunk.nextToken());
					while(stExample.hasMoreTokens())
						vExample.addElement(stExample.nextToken());
					*/
					
					boolean bPreviousWasChunk = false, bStarved = false;
					String /*strTemp = new String(), */strExCheck = new String();
					Object oTemp = null, oComp = null;
					int eNum = 0;
					boolean bNonMatch = false;
					for(int cNum=0; (cNum<cCheck.size() || bStarved) && !bNonMatch; cNum++) {
						System.out.println("===");
						oComp = cCheck.get(cNum);
						
						
						if(oComp instanceof String) {
							/*if(eNum>=vExample.size())
									return "["+strDomain+"]";
							*/strExCheck = (String)vExample.elementAt(eNum);
							System.out.println("check 1.2.1");
							int ctr = eNum;
				/*
							while(!oComp.toString().equalsIgnoreCase(strExCheck) 
								&& ctr+1<vExample.size()) {
								ctr++;
								strExCheck = (String)vExample.elementAt(ctr);
	System.out.println("example check:"+strExCheck);
							}
				*/
							if(oComp.toString().equalsIgnoreCase(strExCheck)) {
								eNum = ctr;
								cChunk.addElement(oComp.toString());
								sbChunk.append(oComp.toString()).append(' ');
								eNum++;
							} else
								bNonMatch = true;
							 /*else // **MOST PROBLEMATIC TO ANALYZE ITS USAGE**
								return "["+strDomain+"]";*/
							bStarved = false;
							bPreviousWasChunk = false;
						} else {
							String strNextConst = null;
							String strNextExampleConst = null;
							boolean bPreStarved = false;
							if(cNum<cCheck.size()-1) {
								strNextConst = (String)cCheck.get(cNum+1);
								bPreStarved = true;
							}
							Vector vChunkEx = new Vector();
							//StringBuffer sbChunkExample = new StringBuffer();
							if(strNextConst != null) {
								strNextExampleConst = (String)vExample.get(eNum);
								eNum++;
								do {
									vChunkEx.addElement(strNextExampleConst);
									//sbChunkExample.append(strNextExampleConst);
									if(eNum<vExample.size()) {
										strNextExampleConst = (String)vExample.get(eNum);
										eNum++;
									}
									/*if(strNextConst!= null && 
										!strNextConst.equals(strNextExampleConst))
										sbChunkExample.append(' ');*/
								} while(eNum<vExample.size() && 
									!strNextConst.equalsIgnoreCase(strNextExampleConst));
								/* This is on the assumption that there is always at
								 *	least one constant in between chunks; therefore
								 *	the next token is a constant.
								 */
								eNum--;
							} else {
								while(eNum<vExample.size()) {
									vChunkEx.addElement(vExample.elementAt(eNum).toString());
									//sbChunkExample.append(vExample.elementAt(eNum).toString());
									/*if(eNum != vExample.size()-1)
										sbChunkExample.append(' ');*/
									eNum++;
								}
							}
							
							
							String strDom = oComp.toString().substring(1,oComp.toString().length()-1);
			/*		System.out.println("to recurse:"+sbChunkExample.toString()
								+","+strDom);
			*/				oTemp = exploreChunk((Vector)vChunkEx.clone(),	strDom,	bIsForSource);
							String strOldDomain = (String)csMemory.pop();
							if(oTemp instanceof ChunkPair) {
								System.out.println("domain:"+strDom+"; source:"
									+((ChunkPair)oTemp).getSourceStr()+"; target:"
									+((ChunkPair)oTemp).getTargetStr());
								if(bIsForSource) {
									cChunk.addAll(((ChunkPair)oTemp).getSourceChunk());
									sbChunk.append(((ChunkPair)oTemp).getSourceStr());
						/*			htAccessTable.put(strDom, 
										((ChunkPair)oTemp).getSourceStr());
						*/		} else {
									cChunk.addAll(((ChunkPair)oTemp).getTargetChunk());
									sbChunk.append(((ChunkPair)oTemp).getTargetStr());
						/*			htAccessTable.put(strDom, 
										((ChunkPair)oTemp).getTargetStr());
						*/		}
								// should it be the other way around? I think not...
							} else if(oTemp instanceof String) {
								cChunk.addElement(oTemp.toString());
								sbChunk.append(oTemp.toString());
							}
							if(/*!stChunk.hasMoreTokens() && */bPreStarved) 
								/* Observe whether the condition '!stChunk.hasMoreTokens()' 
								 *	still matters would still have an impact now that the
								 *	tokens are now treated as Vector objects.
								 */
								bStarved = true;
							bPreStarved = false;
							bPreviousWasChunk = true;
							oComp = strNextConst;
							strExCheck = strNextExampleConst;
						}
						
						if(bStarved)
							sbChunk.append(' ');
			/*			System.out.println("chunk domain "+strDomain+" so far looks like this: "+sbChunk.toString());
						// ADDITION
						if(sbChunk.toString().contains("[") && sbChunk.toString().contains("]"))
							return "["+strDomain+"]";
						// ADDITION				*/
					}
					if(oTemp instanceof String || oTemp == null)
						continue;
					if(bNonMatch)
						return "["+strDomain+"]";
					/* At this point, it must be assumed that a chunk matches
					 * exactly to all subchunk elements. Otherwise, the code
					 * skips to the next possible chunk to explore.
					 */

					String strChunk = sbChunk.toString();
					System.out.println("'"+strChunk+"'"+strChunk.length());
					
					if(strChunk.equals(""))
						strChunk = new String(oComp.toString());
					strChunk = strChunk.trim();
					
					if(bIsForSource) {
						htAccessTable.put(strDomain, cChunk);
						return new ChunkPair(cChunk, new TExtChunk(), strDomain);
					} else {
						htAccessTable.put(strDomain, cChunk);
						return new ChunkPair(new TExtChunk(), cChunk, strDomain);
					}
				}
			}	
		} catch(SQLException sqle) {
			System.err.println("SQL Error: "+sqle.getMessage());
		}
		if(csMemory.size()==1) {
			String strChunk = sbChunk.toString();
			System.out.println("'"+strChunk+"'"+strChunk.length());
			/*
			if(strChunk.equals(""))
				strChunk = strStuff;
			strChunk = strChunk.trim();
			*/
/*			
			if(!dbConnection.isClosed())
				disconnectDB();
*/			
			if(bIsForSource)
				return new ChunkPair(cChunk, new TExtChunk(), strDomain);
			else
				return new ChunkPair(new TExtChunk(), cChunk, strDomain);
		}
				
		/* So far, the only time this point is reached is when there are totally
		 * no matches for the chunk given the example. Given such a case, just
		 * return the chunk label.
		 */
		return "["+strDomain+"]";
	}
	
	/**
	 * NOTE: Use this for mining chunks in the source language first before
	 * using for the target language.
	 *
	 * @param String strExample the word from the example to try to find a match
	 *	for in the chunk of the parameter domain
	 * @param String strDomain the domain of the chunk to explore
	 * @param boolean flag indicating the language of the chunk being explored
	 *
	 * @return ChunkPair the constant form representation of the parameter chunk
	 *	in both English and Filipino
	 *
	 * @deprecated as of ver. 1.01.01
	 */
	public Object retrieveChunk(String strExample, String strDomain,
		boolean bIsForSource) {
		ChunkPair wpChunkData = new ChunkPair(new String(),new String(), new String());
		
		System.out.println("NEW RECURSION: "+strExample+"\t"+strDomain);
		csMemory.push(strDomain);
		System.out.println("stack size:"+csMemory.size());
		String strStmt = new String();
		ResultSet rsSet;
				
		if(strDomain.contains("."))
			strStmt = "Select * From Chunks Where domain='" + strDomain + "'";
		else 
			strStmt = "Select * From Chunks Where domain Like '" + strDomain 
				+ ".%'";
				
		String strSource, strTarget;
		if(bSourceIsFilipino) {
			strSource = "chunkFilipino";
			strTarget = "chunkEnglish";
		} else {
			strSource = "chunkEnglish";
			strTarget = "chunkFilipino";
		}
				

		rsSet = DBConnector.select(strStmt);
		
		StringBuffer sbChunk = new StringBuffer();
		try {
			while(rsSet.next()) {
				String strChunkS = rsSet.getString(strSource);
				String strChunkT = rsSet.getString(strTarget);
				
				System.out.println("check 1");
				
				String strCheck;
				if(bIsForSource)
					strCheck = strChunkS;
				else
					strCheck = strChunkT;
				
				if(!strChunkS.contains("[") && !strChunkT.contains("[")) {//meaning there's no subchunk
					System.out.println("check 1.1");
					/*System.out.println("source:"+strChunkS+"; target:"+strChunkT
						+"; example:"+strExample);*/
					if(strCheck.equalsIgnoreCase(strExample))
						return new ChunkPair(strChunkS, strChunkT, strDomain);
				} else {
					System.out.println("check 1.2");
					System.out.println("check:"+strCheck);
					StringTokenizer stChunk = new StringTokenizer(strCheck);
					StringTokenizer stExample = new StringTokenizer(strExample);
					Vector vChunk = new Vector(), vExample = new Vector();
					while(stChunk.hasMoreTokens())
						vChunk.addElement(stChunk.nextToken());
					while(stExample.hasMoreTokens())
						vExample.addElement(stExample.nextToken());
					
					boolean bPreviousWasChunk = false, bStarved = false;
					String strTemp = new String(), strExCheck = new String();
					Object oTemp = null;
					int eNum = 0;
					for(int cNum=0; cNum<vChunk.size() || bStarved; cNum++) {
						System.out.println("===");
						strTemp = (String)vChunk.get(cNum);
													
						
	System.out.println("temp:"+strTemp);
						if(!strTemp.startsWith("[")) {
							/*if(eNum>=vExample.size())
									return "["+strDomain+"]";
							*/
							strExCheck = (String)vExample.get(eNum);
							System.out.println("check 1.2.1");
	System.out.println("example check:"+strExCheck);
							int ctr = eNum;
							while(!strTemp.equalsIgnoreCase(strExCheck) 
								&& ctr+1<vExample.size()) {
								ctr++;
								strExCheck = (String)vExample.get(ctr);
							}
							if(strTemp.equalsIgnoreCase(strExCheck)) {
								eNum = ctr;
								sbChunk.append(strTemp);
								sbChunk.append(' ');
								eNum++;
							} /*else // **MOST PROBLEMATIC TO ANALYZE ITS USAGE**
								return "["+strDomain+"]";*/
							bStarved = false;
							bPreviousWasChunk = false;
						} else {
							System.out.println("check 1.2.2");
							String strNextConst = null;
							String strNextExampleConst = null;
							boolean bPreStarved = false;
							if(cNum<vChunk.size()-1) {
								strNextConst = (String)vChunk.get(cNum+1);
								bPreStarved = true;
							}
							StringBuffer sbChunkExample = new StringBuffer();
							if(strNextConst != null) {
								strNextExampleConst = (String)vExample.get(eNum);
								eNum++;
					System.out.println("1:"+strNextConst+":"+strNextExampleConst);
								do {
									sbChunkExample.append(strNextExampleConst);
									if(eNum<vExample.size()) {
										strNextExampleConst = (String)vExample.get(eNum);
										eNum++;
									}
									if(strNextConst!= null && 
										!strNextConst.equalsIgnoreCase(strNextExampleConst))
										sbChunkExample.append(' ');
								} while(eNum<vExample.size() && 
									!strNextConst.equalsIgnoreCase(strNextExampleConst));
								/* This is on the assumption that there is always at
								 *	least one constant in between chunks; therefore
								 *	the next token is a constant.
								 */
								eNum--;
							} else {
								while(eNum<vExample.size()) {
									sbChunkExample.append(vExample.elementAt(eNum).toString());
									if(eNum != vExample.size()-1)
										sbChunkExample.append(' ');
									eNum++;
								}
							}
							
							
							String strDom = strTemp.substring(1,strTemp.length()-1);
			/*		System.out.println("to recurse:"+sbChunkExample.toString()
								+","+strDom);
			*/				oTemp = retrieveChunk(sbChunkExample.toString(), 
								strDom,	bIsForSource);
					System.out.println("RETURN FROM RECURSION");
							String strOldDomain = (String)csMemory.pop();
							System.out.println("previous recursion was of: "+strOldDomain);
							System.out.println("stack size:"+csMemory.size());
							if(oTemp instanceof ChunkPair) {
								/*System.out.println("domain:"+strDom+"; source:"
									+((ChunkPair)oTemp).getSourceStr()+"; target:"
									+((ChunkPair)oTemp).getTargetStr());*/
	System.out.println("added item of domain "+strDom);
								if(bIsForSource) {
									sbChunk.append(((ChunkPair)oTemp).getSourceStr());
						/*			htAccessTable.put(strDom, 
										((ChunkPair)oTemp).getSourceStr());
						*/		} else {
									sbChunk.append(((ChunkPair)oTemp).getTargetStr());
						/*			htAccessTable.put(strDom, 
										((ChunkPair)oTemp).getTargetStr());
						*/		}
								// should it be the other way around? I think not...
							} else if(oTemp instanceof String)
								sbChunk.append(oTemp.toString());
							if(!stChunk.hasMoreTokens() && bPreStarved)
								bStarved = true;
							bPreStarved = false;
							bPreviousWasChunk = true;
							strTemp = strNextConst;
							strExCheck = strNextExampleConst;
						}
						
						if(bStarved)
							sbChunk.append(' ');
						System.out.println("chunk domain "+strDomain+" so far looks like this: "+sbChunk.toString());
					}
					if(oTemp instanceof String || oTemp == null)
						continue;
					System.out.println("check 1.X");
					/* At this point, it must be assumed that a chunk matches
					 * exactly to all subchunk elements. Otherwise, the code
					 * skips to the next possible chunk to explore.
					 */

					String strChunk = sbChunk.toString();
					System.out.println("'"+strChunk+"'"+strChunk.length());
					
					if(strChunk.equals(""))
						strChunk = new String(strTemp);

					if(sbChunk.charAt(sbChunk.length()-1) == ' ')
						sbChunk.deleteCharAt(sbChunk.length()-1);

					if(bIsForSource) {
	System.out.println(">>"+strDomain+":"+((ChunkPair)oTemp).getSourceStr());
						htAccessTable.put(strDomain, sbChunk.toString());
						return new ChunkPair(sbChunk.toString(),"T-DUMMY", strDomain);
					} else {
	System.out.println(">>"+strDomain+":"+((ChunkPair)oTemp).getTargetStr());
						htAccessTable.put(strDomain, sbChunk.toString());
						return new ChunkPair("S-DUMMY",sbChunk.toString(), strDomain);
					}
				}
			}	
		} catch(SQLException sqle) {
			System.err.println("SQL Error: "+sqle.getMessage());
		}
			
		if(csMemory.size()==1)
			if(bIsForSource)
				return new ChunkPair(sbChunk.toString(),"T-DUMMY", strDomain);
			else
				return new ChunkPair("S-DUMMY",sbChunk.toString(), strDomain);
				
		System.out.println("Ultimate Return Point");
		/* So far, the only time this point is reached is when there are totally
		 * no matches for the chunk given the example. Given such a case, just
		 * return the chunk label.
		 */
		return "["+strDomain+"]";
	}
	
	
	public static void main(String[] args) {
		DBConnector.connect();
		ChunkMiner cmTest = new ChunkMiner(false, new TExtLearnerUtility(false));
		
		//Object oTest = cmTest.exploreChunk("danced and worked", "999.1", true); // Sample 1 - X
		//Object oTest = cmTest.exploreChunk("always ate", "998.1", true); // Sample 2 - O
		//Object oTest = cmTest.exploreChunk("always slept before this", "996.1", true); // Sample 3 - O
		//Object oTest = cmTest.exploreChunk("never gleefully danced all that", "997.1", true); // Sample 4 - O
		//Object oTest = cmTest.exploreChunk("never sang and danced all this", "997.1", true); // Sample 5 - X
		//Object oTest = cmTest.exploreChunk("never drank all of it", "997.1", true); // Sample 6 - X
		//Object oTest = cmTest.exploreChunk("never hysterically danced all that", "997.1", true); // Sample 7 - X
		//Object oTest = cmTest.exploreChunk("often laughed and played and drew and wrote and told stories", "994.1", true); // Sample 8 - O
		//Object oTest = cmTest.exploreChunk("often laughed and played and drew and wrote and reported", "994.1", true); // Sample 9 - X
		//Object oTest = cmTest.exploreChunk("often laughed and told stories", "994.1", true); // Sample 10 - X
		//Object oTest = cmTest.exploreChunk("never worked all that", "997.1", true); // Sample 11 - O
		Vector vExample = new Vector();
		String strDomain = new String();
		/*
		// Sample 1 - X
		vExample.addElement("danced");
		vExample.addElement("and");
		vExample.addElement("worked");
		strDomain = "999.1";
		/*
		// Sample 2
		vExample.addElement("always");
		vExample.addElement("ate");
		strDomain = "998.1";
		/*
		// Sample 3
		vExample.addElement("always");
		vExample.addElement("slept");
		vExample.addElement("before");
		vExample.addElement("this");
		strDomain = "996.1";
		/*
		// Sample 4
		vExample.addElement("never");
		vExample.addElement("gleefully");
		vExample.addElement("danced");
		vExample.addElement("all");
		vExample.addElement("that");
		strDomain = "997.1";
		/*
		// Sample 5 - X
		vExample.addElement("never");
		vExample.addElement("sang");
		vExample.addElement("and");
		vExample.addElement("danced");
		vExample.addElement("all");
		vExample.addElement("this");
		strDomain = "997.1";
		/*
		// Sample 6 - X
		vExample.addElement("never");
		vExample.addElement("drank");
		vExample.addElement("all");
		vExample.addElement("of");
		vExample.addElement("it");
		strDomain = "997.1";
		/*
		// Sample 7 - X
		vExample.addElement("never");
		vExample.addElement("hysterically");
		vExample.addElement("danced");
		vExample.addElement("all");
		vExample.addElement("that");
		strDomain = "997.1";
		/*/
		// Sample 8
		vExample.addElement("often");
		vExample.addElement("laughed");
		vExample.addElement("and");
		vExample.addElement("played");
		vExample.addElement("and");
		vExample.addElement("drew");
		vExample.addElement("and");
		vExample.addElement("wrote");
		vExample.addElement("and");
		vExample.addElement("told");
		vExample.addElement("stories");
		strDomain = "994.1";
		/*
		// Sample 9 - X
		vExample.addElement("often");
		vExample.addElement("laughed");
		vExample.addElement("and");
		vExample.addElement("played");
		vExample.addElement("and");
		vExample.addElement("drew");
		vExample.addElement("and");
		vExample.addElement("wrote");
		vExample.addElement("and");
		vExample.addElement("reported");
		strDomain = "994.1";
		/*
		// Sample 10 - X
		vExample.addElement("often");
		vExample.addElement("laughed");
		vExample.addElement("and");
		vExample.addElement("told");
		vExample.addElement("stories");
		strDomain = "994.1";
		/*
		// Sample 11
		vExample.addElement("never");
		vExample.addElement("worked");
		vExample.addElement("all");
		vExample.addElement("that");
		strDomain = "997.1";
		/*
		// Sample 12 - change boolean to false
		vExample.addElement("tumawa");
		vExample.addElement("at");
		vExample.addElement("naglaro");
		vExample.addElement("at");
		vExample.addElement("gumuhit");
		vExample.addElement("at");
		vExample.addElement("nagsulat");
		vExample.addElement("at");
		vExample.addElement("nagkwento");
		vExample.addElement("nang");
		vExample.addElement("madalas");
		strDomain = "994.1";
		/*
		// Sample 13 - X - change boolean to false
		vExample.addElement("tumawa");
		vExample.addElement("at");
		vExample.addElement("naglaro");
		vExample.addElement("at");
		vExample.addElement("nagsulat");
		vExample.addElement("at");
		vExample.addElement("nagkwento");
		vExample.addElement("nang");
		vExample.addElement("madalas");
		strDomain = "994.1";
		/*
		// Sample 14 - change boolean to false
		vExample.addElement("kumanta");
		vExample.addElement("at");
		vExample.addElement("sumayaw");
		vExample.addElement("ni");
		vExample.addElement("kailanman");
		vExample.addElement("lahat");
		vExample.addElement("ng");
		vExample.addElement("iyan");
		strDomain = "997.1";
		
		/*
		*/
		Object oTest = cmTest.exploreChunk(vExample, strDomain, true); 
			// change boolean state (true: English/source; false: Filipino/target)
		
		if(oTest instanceof ChunkPair) {
			System.out.println("Testing: S:" + ((ChunkPair)oTest).getSourceChunk().toString() 
				+ "; T:" + ((ChunkPair)oTest).getTargetChunk().toString());
			System.out.println("Hashtable definition size: "+cmTest.htAccessTable.size());
		}
		
//		cmTest.disconnectDB();
	}
}

class ChunkStack {
	private Vector vStack = new Vector();
	
	public void push(Object oTemp) {
		vStack.addElement(oTemp);
	}
	
	public Object pop() {
		return vStack.remove(vStack.size()-1);
	}
	
	public Object peek() {
		return vStack.elementAt(vStack.size()-1);
	}
	
	public int size() {
		return vStack.size();
	}
}